package com.cnap.multiclouldmgmt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * 线程池配置类
 * corePoolSize 当线程池被创建后，
 * 线程池中就会有指定数量的线程被创建用来等待任务的执行，
 * 这些线程被称为核心线程。即使它们是空闲的，
 * 它们也不会被销毁。除非设置了allowCoreThreadTimeOut参数。
 *
 * maximumPoolSize 线程池所能容纳的最大线程数量。
 *
 * keepAliveTime 用来指定非核心线程被创建后最多可以存活的时间。
 *
 * unit 用来设置上述时间的单位。
 *
 * workQueue 用来保存等待执行的任务。
 *
 * threadFactory（非必填参数，若不填写则使用默认值） 执行程序创建新线程时使用的工厂
 *
 * handler（非必填参数，若不填写则使用默认值） 拒绝策略
 *线程池的5种状态具体如下：
 *
 * （1）RUNNING：线程池创建之后的初始状态，这种状态下可以执行任务。
 *
 * （2）SHUTDOWN：该状态下线程池不再接受新任务，但是会将工作队列中的任务执行完毕。
 *
 * （3）STOP：该状态下线程池不再接受新任务，也不会处理工作队列中的剩余任务，并且将会中断所有工作线程。
 *
 * （4）TIDYING：该状态下所有任务都已终止或者处理完成，将会执行terminated()钩子方法。
 *
 * （5）TERMINATED：执行完terminated()钩子方法之后的状态。terminated钩子方法在Executor终止时调用，默认实现不执行任何操作
 *
 * 线程池的状态转换规则为：
 *
 * （1）线程池创建之后状态为RUNNING。
 *
 * （2）执行线程池的shutdown()实例方法，会使线程池状态从RUNNING转变为SHUTDOWN。
 *
 * （3）执行线程池的shutdownNow()实例方法，会使线程池状态从RUNNING转变为STOP。
 *
 * （4）当线程池处于SHUTDOWN状态时，执行其shutdownNow()方法会将其状态转变为STOP。
 *
 * （5）等待线程池的所有工作线程停止，工作队列清空之后，线程池状态会从STOP转变为TIDYING。
 *
 * （6）执行完terminated()钩子方法之后，线程池状态从TIDYING转变为TERMINATED。
 * AbortPolicy: 默认策略，抛出异常RejectedExecutionException，拒绝执行 。
 *
 * CallerRunsPolicy: 调用执行自己的线程运行任务，也就是直接在调用execute方法的线程中运行(run)被拒绝的任务，如果执行程序已关闭，则会丢弃该任务。因此这种策略会降低对于新任务提交速度，影响程序的整体性能。如果您的应用程序可以承受此延迟并且要求任何一个任务请求都要被执行的话，你可以选择这个策略。
 *
 * DiscardPolicy: 不处理新任务，直接丢弃掉。
 * DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。
 */
@EnableAsync
@Configuration
public class ThreadPoolConfig {

    /** 线程池中的核心线程数量,默认为1 */
    private int corePoolSize = 4;
    /** 线程池中的最大线程数量 */
    private int maxPoolSize = 10;
    /** 线程池中允许线程的空闲时间,默认为 60s */
    private int keepAliveTime = ((int) TimeUnit.SECONDS.toSeconds(30));
    /** 线程池中的队列最大数量 */
    private int queueCapacity = 1000;

    /** 线程的名称前缀 */
    private static final String THREAD_PREFIX = "thread-call-runner-%d";

    @Bean("cnapTaskExcutor")
    @Lazy
    public ThreadPoolTaskExecutor threadPool(){

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setKeepAliveSeconds(keepAliveTime);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix(THREAD_PREFIX);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        executor.initialize();

        return executor;
    }

    /**
     * get
     *
     * @return CorePoolSize
     */
    public int getCorePoolSize() {
        return corePoolSize;
    }

    /**
     * set
     *
     * @param corePoolSize corePoolSize
     */
    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    /**
     * get
     *
     * @return MaxPoolSize
     */
    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    /**
     * set
     *
     * @param maxPoolSize maxPoolSize
     */
    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    /**
     * get
     *
     * @return KeepAliveTime
     */
    public int getKeepAliveTime() {
        return keepAliveTime;
    }

    /**
     * set
     *
     * @param keepAliveTime keepAliveTime
     */
    public void setKeepAliveTime(int keepAliveTime) {
        this.keepAliveTime = keepAliveTime;
    }

    /**
     * get
     *
     * @return QueueCapacity
     */
    public int getQueueCapacity() {
        return queueCapacity;
    }

    /**
     * set
     *
     * @param queueCapacity queueCapacity
     */
    public void setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
    }

    /**
     * get
     *
     * @return ThreadPrefix
     */
    public static String getThreadPrefix() {
        return THREAD_PREFIX;
    }
}
